package main

import (
	"context"
	"net"
	"os"
	"os/signal"
	"path/filepath"
	"strings"
	"syscall"
	"time"

	"vdrift/asset"
	"vdrift/internal/checker"
	"vdrift/internal/config"
	"vdrift/internal/coordinator"
	"vdrift/internal/network/grpc"
	"vdrift/internal/network/udp"
	"vdrift/internal/state"
	"vdrift/internal/vip"
	pb "vdrift/pkg/proto"
	"vdrift/pkg/utils"

	"vdrift/internal/logger"

	"github.com/sirupsen/logrus"
	"github.com/spf13/pflag"
)

var (
	configPath = pflag.StringP("config", "c", "", "config file path")
	version    = pflag.BoolP("version", "v", false, "show version")
)

// asset.VERSION replice \n to empty string
var Version = strings.ReplaceAll(asset.VERSION, "\n", "")

func init() {
	// 初始化logger
	logger.Init(&logger.Config{
		Level:  logger.LevelInfo,
		Format: logger.FormatJSON,
		Output: "stdout",
	})
}

func getConfigPath() string {

	if *configPath == "" {
		currentDirectory := utils.GetExecutableDirectory()
		if currentDirectory == "" {
			return *configPath
		}
		_conf := filepath.Join(currentDirectory, "config.yaml")
		if utils.IsExist(_conf) {
			return _conf
		}
		return *configPath
	}

	if !utils.IsExist(*configPath) {
		logger.Fatalf("config file not found,%v", *configPath)
	}

	return ""

}

func main() {
	pflag.Parse()

	if *version {
		logger.Infof("vdrift %s", Version)
		return
	}

	logrus.SetFormatter(&logrus.JSONFormatter{})
	logrus.SetOutput(os.Stdout)
	logger.Infof("vdrift %s starting...", Version)

	conf := getConfigPath()
	if conf == "" {
		logger.Fatalf("config file not found,%v", *configPath)
		os.Exit(1)
	}
	logger.Infof("config file using,%v", conf)

	cfg, err := config.Load(conf)
	if err != nil {
		logger.WithError(err).Fatal("failed to load config")
	}

	ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
	defer stop()

	// === 1. 获取本机 IP ===
	myIP := getLocalIP(cfg)
	if myIP == "" {
		logger.Fatal("cannot determine local IP")
	}
	logger.WithField("ip", myIP).Info("local IP resolved")

	// === 2. 初始化 Checker ===
	var checkers []checker.Checker
	for _, cc := range cfg.Checkers {
		switch cc.Type {
		case "tcp":
			checkers = append(checkers, checker.NewTCPChecker(
				cc.Name, cc.Host, cc.Port, cc.Timeout.Duration,
			))
		case "mysql":
			checkers = append(checkers, checker.NewMySQLChecker(
				cc.Name, cc.Host, cc.Port,
				cc.Username, cc.Password, cc.Query,
				cc.Timeout.Duration,
			))
		default:
			logger.WithField("type", cc.Type).Fatal("unsupported checker type")
		}
	}
	if len(checkers) == 0 {
		logger.Fatal("no checkers configured")
	}

	pool := checker.NewPool(checkers)
	go pool.Start(ctx)

	// === 3. 本地状态 ===
	localState := &state.LocalState{
		BasePriority:   cfg.Priority.Base,
		WeightPerCheck: cfg.Priority.WeightPerCheck,
	}

	// === 4. VIP Manager ===
	vipManager, err := vip.NewManager(cfg.Interface, cfg.VIP)
	if err != nil {
		logger.WithError(err).Fatal("failed to initialize VIP manager")
	}
	if err := vipManager.WaitForInterface(10 * time.Second); err != nil {
		logger.WithError(err).Warn("interface not ready")
	}

	// === 5. UDP 心跳 ===
	hbStop := make(chan struct{})
	hb := udp.NewHeartbeat(cfg.NodeID, myIP, localState)
	go hb.Start(hbStop)

	// === 6. gRPC 服务器 ===
	stateCh := make(chan pb.NodeState, 10)
	go grpc.StartServer(cfg.Listen.GRPC, stateCh)

	// === 7. gRPC 客户端 ===
	clients := make(map[string]*grpc.Client)
	for _, p := range cfg.Peers {
		if p.ID == cfg.NodeID {
			continue
		}
		cli, err := grpc.NewClient(net.JoinHostPort(p.IP, "9652"))
		if err != nil {
			logger.WithField("peer", p.ID).WithError(err).Warn("gRPC connect failed")
			continue
		}
		clients[p.ID] = cli
	}

	// === 8. 选举协调器 ===
	election := coordinator.NewElection(cfg.NodeID, len(cfg.Peers))
	leaderCh := election.LeaderChan()

	// === 9. 主循环 ===
	ticker := time.NewTicker(1 * time.Second)
	defer ticker.Stop()

	for {
		select {
		case <-ctx.Done():
			for _, c := range clients {
				c.Close()
			}
			close(hbStop)
			return

		case remote := <-stateCh:
			election.Update(remote)

		case leader := <-leaderCh:
			isLeader := leader == cfg.NodeID
			shouldBind := isLeader && pool.IsHealthy()
			bound, _ := vipManager.IsBound()

			if shouldBind && !bound {
				if err := vipManager.Bind(); err != nil {
					logger.WithError(err).Error("failed to bind VIP")
				}
			} else if !shouldBind && bound {
				if err := vipManager.Unbind(); err != nil {
					logger.WithError(err).Error("failed to unbind VIP")
				}
			}

		case <-ticker.C:
			// 更新本地状态
			localState.FailedChecks = pool.GetFailedCount()

			// 推送状态
			pbState := &pb.NodeState{
				NodeId:    cfg.NodeID,
				Ip:        myIP,
				Priority:  int32(localState.CalculatePriority()),
				Healthy:   pool.IsHealthy(),
				Timestamp: time.Now().UnixNano(),
				Seq:       hb.GetSeq(), // 修复：通过方法获取
				VipBound:  election.GetLeader() == cfg.NodeID,
			}
			for _, c := range clients {
				go c.SendState(pbState)
			}
		}
	}
}

// 获取本机 IP（匹配 config.peers）
func getLocalIP(cfg *config.Config) string {
	for _, p := range cfg.Peers {
		if p.ID == cfg.NodeID {
			return p.IP
		}
	}
	return ""
}
